Figure 8. Ratio of the maximum value of the peaks measured by the sensors against the maximum value of the peaks measured by the DustTrak before and after calibration for each experiment. The box and whisker plot horizontal lines represent, from bottom to top, the lower quartile, the median and the upper quartile. The vertical lines are drawn to the smallest and the largest data point that fall within 1.5 times the interquartile range below the lower quartile and above the upper quartile respectively. Values outside these range are considered as outliers and plotted as points.
source("utilities.R")
source("variables.R")
require(ggplot2)
require(plotly)
require(cowplot)
require(splitstackshape)
require(ggpubr)
source("Table_2_rh_exploration.R")
df <- prepare_sensor_data(sensor_delay_corrected, cdt)
dt <- prepare_dusttrak_data(dusttrak_file, cdt)
df %>%
select(sensor, exp, source, date, PM25) %>%
filter(sensor!="SHT35") %>%
filter(exp!="") %>%
group_by(exp, source, sensor) %>%
nest() -> tmp
tmp %>%
dplyr::mutate(regression = map(data, ~clean_regression(df =.x,dusttrak = dt, 0))) %>%
unnest(regression, .drop=TRUE) ->res
res<-cSplit(indt=res,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE) %>%
select(-sensor_2,-sensor_3)->res
res <- res %>%
flag_site_data(sensor_list)
res_rh_temp <- inner_join(res, summary_rh_temp, by = c("exp"))
#write.csv(select(res_rh_temp,-data), "C:/GitHub/AQ_analysis_lab/lm_no_enclosure_cdt_corr.csv")
res_rh_temp <- res_rh_temp %>%
mutate(rh_median = round2(rh_median,0))
df %>% inner_join(select(res_rh_temp, sensor, exp, source, slope, intercept, slope_p_value), by = c("sensor", "exp", "source")) -> df_coeff
df_coeff %>%
mutate(PM25_corr = ifelse(slope_p_value<=0.05,(PM25-intercept)/slope), -999) %>%
filter(PM25_corr != -999) -> df_calibrated
df_calibrated<-cSplit(indt=df_calibrated,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE) %>%
select(-sensor_2,-sensor_3)
What does it looks like?
p_opcr1 <- ggplot()+
geom_line(data = filter(df_calibrated, sensor_1=="OPCR1", exp != ""),
aes(x = date, y = PM25_corr, group = sensor, colour = sensor,
text = paste0("Box: ", site,
"<br>Exp: ", exp,
"<br>Source: ", source))) +
labs(y = "PM2.5 (ug/m3)") +
geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
scale_x_datetime() + theme_bw() + ggtitle("OPCR1") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_pms5003 <- ggplot()+
geom_line(data = filter(df_calibrated, sensor_1=="PMS5003", exp != ""),
aes(x = date, y = PM25_corr, group = sensor, colour = sensor,
text = paste0("Box: ", site,
"<br>Exp: ", exp,
"<br>Source: ", source))) +
labs(y = "PM2.5 (ug/m3)") +
geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
scale_x_datetime() + theme_bw() + ggtitle("PMS5003") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_sds018 <- ggplot()+
geom_line(data = filter(df_calibrated, sensor_1=="SDS018", exp != ""),
aes(x = date, y = PM25_corr, group = sensor, colour = sensor,
text = paste0("Box: ", site,
"<br>Exp: ", exp,
"<br>Source: ", source))) +
labs(y = "PM2.5 (ug/m3)") +
geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
scale_x_datetime() + theme_bw() + ggtitle("SDS018") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_sps030 <- ggplot()+
geom_line(data = filter(df_calibrated, sensor_1=="SPS030", exp != ""),
aes(x = date, y = PM25_corr, group = sensor, colour = sensor,
text = paste0("Box: ", site,
"<br>Exp: ", exp,
"<br>Source: ", source))) +
labs(y = "PM2.5 (ug/m3)") +
geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
scale_x_datetime() + theme_bw() + ggtitle("SPS030") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_hpma <- ggplot()+
geom_line(data = filter(df_calibrated, sensor_1=="HPMA115S0", exp != ""),
aes(x = date, y = PM25_corr, group = sensor, colour = sensor,
text = paste0("Box: ", site,
"<br>Exp: ", exp,
"<br>Source: ", source))) +
labs(y = "PM2.5 (ug/m3)") +
geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
scale_x_datetime() + theme_bw() + ggtitle("HPMA115S0") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
ggplotly(p_opcr1, dynamicTicks = TRUE)
ggplotly(p_pms5003, dynamicTicks = TRUE)
ggplotly(p_sds018, dynamicTicks = TRUE)
ggplotly(p_sps030, dynamicTicks = TRUE)
ggplotly(p_hpma, dynamicTicks = TRUE)
Now we extract the peaks.
peaks<-readRDS(file="datasets/peaks_characteristics.rds")
res <- df_calibrated %>%
filter(date>=as.POSIXct(paste("2019-09-05", peaks[1,]$Start),tz="UTC"), date<=as.POSIXct(paste("2019-09-05", peaks[1,]$End),tz="UTC")) %>%
group_by(sensor) %>%
filter(PM25>=1) %>%
summarise(max_value = max(PM25_corr)) %>%
mutate(Experiment = peaks[1,]$Experiment, Source = peaks[1,]$Source, Variation= peaks[1,]$Variation, Number = peaks[1,]$Number)
for(row in 2:nrow(peaks)){
df_calibrated %>%
filter(date>=as.POSIXct(paste("2019-09-05", peaks[row,]$Start),tz="UTC"), date<=as.POSIXct(paste("2019-09-05", peaks[row,]$End),tz="UTC")) %>%
group_by(sensor) %>%
filter(PM25>=1) %>%
summarise(max_value = max(PM25_corr)) %>%
mutate(Experiment = peaks[row,]$Experiment, Source = peaks[row,]$Source, Variation= peaks[row,]$Variation, Number = peaks[row,]$Number) -> tmp
res<-rbind(res,tmp)
}
res %>%
inner_join(peaks, by=c("Source", "Experiment", "Variation", "Number")) ->res_bind
res_bind<-res_bind %>%
mutate(ratio = max_value/Concentration)
df2<-cSplit(indt=res_bind,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE)
df2 %>%
select(-sensor_2,-sensor_3)->res_bin
Compare with uncalibrated data
res <- df %>%
filter(date >= as.POSIXct(paste("2019-09-05", peaks[1,]$Start), tz = "UTC"),
date <= as.POSIXct(paste("2019-09-05", peaks[1,]$End), tz = "UTC")) %>%
group_by(sensor) %>%
filter(PM25 >= 1) %>%
summarise(max_value = max(PM25)) %>%
mutate(Experiment = peaks[1, ]$Experiment, Source = peaks[1, ]$Source, Variation = peaks[1, ]$Variation, Number = peaks[1, ]$Number)
for(row in 2:nrow(peaks)){
df %>%
filter(date >= as.POSIXct(paste("2019-09-05", peaks[row, ]$Start), tz = "UTC"),
date <= as.POSIXct(paste("2019-09-05", peaks[row, ]$End), tz = "UTC")) %>%
group_by(sensor) %>%
filter(PM25 >= 1) %>%
summarise(max_value = max(PM25)) %>%
mutate(Experiment = peaks[row, ]$Experiment, Source = peaks[row, ]$Source,
Variation= peaks[row, ]$Variation, Number = peaks[row, ]$Number) -> tmp
res<-rbind(res,tmp)
}
res_bind_uncalibrated<- res %>%
inner_join(peaks, by = c("Source", "Experiment", "Variation", "Number")) %>%
mutate(ratio = max_value/Concentration)
res_bind$status <- "Calibrated"
res_bind_uncalibrated$status <- "Not Calibrated"
res_comparison <- rbind(res_bind, res_bind_uncalibrated)
df2<-cSplit(indt=res_comparison,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE)
df2 %>%
select(-sensor_2,-sensor_3)->res_comparison
res_comparison$sensor_1 <- factor(res_comparison$sensor_1, levels = c("PMS5003", "SPS030", "HPMA115S0", "SDS018", "OPCR1"))
res_comparison$status <- factor(res_comparison$status, levels = c("Not Calibrated", "Calibrated"))
res_comparison %>%
filter(Variation == "Peak") %>%
ggplot(aes(x=Experiment,y=ratio,fill=status)) +
geom_boxplot(width=0.8,position=position_dodge(width = 0.8),alpha=0.8,lwd=0.3,fatten=0.8,outlier.size = 0.8,outlier.alpha=0.5,outlier.stroke=0)+
facet_wrap(sensor_1~Source,ncol=4)+ylab("Ratio sensor/dusttrak")+theme_bw()+
scale_y_continuous(breaks=pretty(c(-1,6), n=14),sec.axis = dup_axis(name=NULL))+
theme(axis.text.x = element_text(angle=90,size = 6))->p
p_legend<-get_legend(p)
plot_grid(p_legend)


p<-p+theme(legend.position = "none")
p<-p+ theme(panel.spacing = unit(0, "lines")) +
theme(plot.margin = unit(c(0,0,0,0), "lines"))
p
ggsave(p,filename = "ratio_comparison_spacing.svg",unit = "mm", width= 180, height = 240)
ggsave(as_ggplot(p_legend),filename = "ratio_comparison_legend.svg",unit = "mm", width= 180, height = 240)

NA
NA
LS0tDQp0aXRsZTogIkZpZ3VyZSA4IC0gQ2FsaWJyYXRpb24gb2YgdGhlIHNlbnNvcnMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCkZpZ3VyZSA4LiBSYXRpbyBvZiB0aGUgbWF4aW11bSB2YWx1ZSBvZiB0aGUgcGVha3MgbWVhc3VyZWQgYnkgdGhlIHNlbnNvcnMgYWdhaW5zdCB0aGUgbWF4aW11bQ0KdmFsdWUgb2YgdGhlIHBlYWtzIG1lYXN1cmVkIGJ5IHRoZSBEdXN0VHJhayBiZWZvcmUgYW5kIGFmdGVyIGNhbGlicmF0aW9uIGZvciBlYWNoIGV4cGVyaW1lbnQuIFRoZSBib3gNCmFuZCB3aGlza2VyIHBsb3QgaG9yaXpvbnRhbCBsaW5lcyByZXByZXNlbnQsIGZyb20gYm90dG9tIHRvIHRvcCwgdGhlIGxvd2VyIHF1YXJ0aWxlLCB0aGUgbWVkaWFuIGFuZCB0aGUNCnVwcGVyIHF1YXJ0aWxlLiBUaGUgdmVydGljYWwgbGluZXMgYXJlIGRyYXduIHRvIHRoZSBzbWFsbGVzdCBhbmQgdGhlIGxhcmdlc3QgZGF0YSBwb2ludCB0aGF0IGZhbGwgd2l0aGluDQoxLjUgdGltZXMgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgYmVsb3cgdGhlIGxvd2VyIHF1YXJ0aWxlIGFuZCBhYm92ZSB0aGUgdXBwZXIgcXVhcnRpbGUgcmVzcGVjdGl2ZWx5Lg0KVmFsdWVzIG91dHNpZGUgdGhlc2UgcmFuZ2UgYXJlIGNvbnNpZGVyZWQgYXMgb3V0bGllcnMgYW5kIHBsb3R0ZWQgYXMgcG9pbnRzLg0KDQpgYGB7ciBzZXR1cH0NCg0Kc291cmNlKCJ1dGlsaXRpZXMuUiIpDQpzb3VyY2UoInZhcmlhYmxlcy5SIikNCnJlcXVpcmUoZ2dwbG90MikNCnJlcXVpcmUocGxvdGx5KQ0KcmVxdWlyZShjb3dwbG90KQ0KcmVxdWlyZShzcGxpdHN0YWNrc2hhcGUpDQpyZXF1aXJlKGdncHVicikNCnNvdXJjZSgiVGFibGVfMl9yaF9leHBsb3JhdGlvbi5SIikNCg0KDQpgYGANCg0KDQpgYGB7cn0NCg0KZGYgPC0gcHJlcGFyZV9zZW5zb3JfZGF0YShzZW5zb3JfZGVsYXlfY29ycmVjdGVkLCBjZHQpDQpkdCA8LSBwcmVwYXJlX2R1c3R0cmFrX2RhdGEoZHVzdHRyYWtfZmlsZSwgY2R0KQ0KDQpkZiAlPiUNCiAgc2VsZWN0KHNlbnNvciwgZXhwLCBzb3VyY2UsIGRhdGUsIFBNMjUpICU+JQ0KICBmaWx0ZXIoc2Vuc29yIT0iU0hUMzUiKSAlPiUNCiAgZmlsdGVyKGV4cCE9IiIpICU+JQ0KICBncm91cF9ieShleHAsIHNvdXJjZSwgc2Vuc29yKSAlPiUNCiAgbmVzdCgpIC0+IHRtcA0KDQp0bXAgJT4lDQogIGRwbHlyOjptdXRhdGUocmVncmVzc2lvbiA9IG1hcChkYXRhLCB+Y2xlYW5fcmVncmVzc2lvbihkZiA9LngsZHVzdHRyYWsgPSBkdCwgMCkpKSAlPiUNCiAgdW5uZXN0KHJlZ3Jlc3Npb24sIC5kcm9wPVRSVUUpICAtPnJlcw0KDQoNCg0KcmVzPC1jU3BsaXQoaW5kdD1yZXMsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMyktPnJlcw0KDQpyZXMgPC0gcmVzICU+JQ0KICBmbGFnX3NpdGVfZGF0YShzZW5zb3JfbGlzdCkNCg0KcmVzX3JoX3RlbXAgPC0gaW5uZXJfam9pbihyZXMsIHN1bW1hcnlfcmhfdGVtcCwgYnkgPSBjKCJleHAiKSkNCg0KDQojd3JpdGUuY3N2KHNlbGVjdChyZXNfcmhfdGVtcCwtZGF0YSksICJDOi9HaXRIdWIvQVFfYW5hbHlzaXNfbGFiL2xtX25vX2VuY2xvc3VyZV9jZHRfY29yci5jc3YiKQ0KDQpyZXNfcmhfdGVtcCA8LSByZXNfcmhfdGVtcCAlPiUNCiAgbXV0YXRlKHJoX21lZGlhbiAgPSByb3VuZDIocmhfbWVkaWFuLDApKQ0KYGBgDQoNCg0KYGBge3J9DQpkZiAlPiUgaW5uZXJfam9pbihzZWxlY3QocmVzX3JoX3RlbXAsIHNlbnNvciwgZXhwLCBzb3VyY2UsIHNsb3BlLCBpbnRlcmNlcHQsIHNsb3BlX3BfdmFsdWUpLCBieSA9IGMoInNlbnNvciIsICJleHAiLCAic291cmNlIikpIC0+IGRmX2NvZWZmDQoNCmRmX2NvZWZmICU+JSANCiAgbXV0YXRlKFBNMjVfY29yciA9IGlmZWxzZShzbG9wZV9wX3ZhbHVlPD0wLjA1LChQTTI1LWludGVyY2VwdCkvc2xvcGUpLCAtOTk5KSAlPiUNCiAgZmlsdGVyKFBNMjVfY29yciAhPSAtOTk5KSAtPiBkZl9jYWxpYnJhdGVkDQoNCmRmX2NhbGlicmF0ZWQ8LWNTcGxpdChpbmR0PWRmX2NhbGlicmF0ZWQsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMykNCmBgYA0KDQpXaGF0IGRvZXMgaXQgbG9va3MgbGlrZT8NCg0KYGBge3J9DQpwX29wY3IxIDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09Ik9QQ1IxIiwgZXhwICE9ICIiKSwNCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZSwgeSA9IFBNMjVfY29yciwgZ3JvdXAgPSBzZW5zb3IsIGNvbG91ciA9IHNlbnNvciwgDQogICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMCgiQm94OiAiLCBzaXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5FeHA6ICIsIGV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U291cmNlOiAiLCBzb3VyY2UpKSkgKw0KICBsYWJzKHkgPSAiUE0yLjUgKHVnL20zKSIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZHQsIGV4cCAhPSAiIiksIGFlcyh4ID0gZGF0ZSx5ID0gcG0yLjUpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIHNjYWxlX3hfZGF0ZXRpbWUoKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJPUENSMSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK2ZhY2V0X3dyYXAofmV4cCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQoNCnBfcG1zNTAwMyA8LSBnZ3Bsb3QoKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZGZfY2FsaWJyYXRlZCwgc2Vuc29yXzE9PSJQTVM1MDAzIiwgZXhwICE9ICIiKSwNCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZSwgeSA9IFBNMjVfY29yciwgZ3JvdXAgPSBzZW5zb3IsIGNvbG91ciA9IHNlbnNvciwgDQogICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMCgiQm94OiAiLCBzaXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5FeHA6ICIsIGV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U291cmNlOiAiLCBzb3VyY2UpKSkgKw0KICBsYWJzKHkgPSAiUE0yLjUgKHVnL20zKSIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZHQsIGV4cCAhPSAiIiksIGFlcyh4ID0gZGF0ZSx5ID0gcG0yLjUpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIHNjYWxlX3hfZGF0ZXRpbWUoKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQTVM1MDAzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfc2RzMDE4IDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09IlNEUzAxOCIsIGV4cCAhPSAiIiksDQogICAgICAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBQTTI1X2NvcnIsIGdyb3VwID0gc2Vuc29yLCBjb2xvdXIgPSBzZW5zb3IsIA0KICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoIkJveDogIiwgc2l0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+RXhwOiAiLCBleHAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlNvdXJjZTogIiwgc291cmNlKSkpICsNCiAgbGFicyh5ID0gIlBNMi41ICh1Zy9tMykiKSArDQogIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGR0LCBleHAgIT0gIiIpLCBhZXMoeCA9IGRhdGUseSA9IHBtMi41KSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKw0KICBzY2FsZV94X2RhdGV0aW1lKCkgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiU0RTMDE4IikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfc3BzMDMwIDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09IlNQUzAzMCIsIGV4cCAhPSAiIiksDQogICAgICAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBQTTI1X2NvcnIsIGdyb3VwID0gc2Vuc29yLCBjb2xvdXIgPSBzZW5zb3IsIA0KICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoIkJveDogIiwgc2l0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+RXhwOiAiLCBleHAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlNvdXJjZTogIiwgc291cmNlKSkpICsNCiAgbGFicyh5ID0gIlBNMi41ICh1Zy9tMykiKSArDQogIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGR0LCBleHAgIT0gIiIpLCBhZXMoeCA9IGRhdGUseSA9IHBtMi41KSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKw0KICBzY2FsZV94X2RhdGV0aW1lKCkgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiU1BTMDMwIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfaHBtYSA8LSBnZ3Bsb3QoKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZGZfY2FsaWJyYXRlZCwgc2Vuc29yXzE9PSJIUE1BMTE1UzAiLCBleHAgIT0gIiIpLA0KICAgICAgICAgICAgYWVzKHggPSBkYXRlLCB5ID0gUE0yNV9jb3JyLCBncm91cCA9IHNlbnNvciwgY29sb3VyID0gc2Vuc29yLCANCiAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKCJCb3g6ICIsIHNpdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkV4cDogIiwgZXhwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5Tb3VyY2U6ICIsIHNvdXJjZSkpKSArDQogIGxhYnMoeSA9ICJQTTIuNSAodWcvbTMpIikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkdCwgZXhwICE9ICIiKSwgYWVzKHggPSBkYXRlLHkgPSBwbTIuNSksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgc2NhbGVfeF9kYXRldGltZSgpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkhQTUExMTVTMCIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK2ZhY2V0X3dyYXAofmV4cCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQpnZ3Bsb3RseShwX29wY3IxLCBkeW5hbWljVGlja3MgPSBUUlVFKQ0KZ2dwbG90bHkocF9wbXM1MDAzLCBkeW5hbWljVGlja3MgPSBUUlVFKQ0KZ2dwbG90bHkocF9zZHMwMTgsIGR5bmFtaWNUaWNrcyA9IFRSVUUpDQpnZ3Bsb3RseShwX3NwczAzMCwgZHluYW1pY1RpY2tzID0gVFJVRSkNCmdncGxvdGx5KHBfaHBtYSwgZHluYW1pY1RpY2tzID0gVFJVRSkNCmBgYA0KDQpOb3cgd2UgZXh0cmFjdCB0aGUgcGVha3MuDQoNCmBgYHtyfQ0KcGVha3M8LXJlYWRSRFMoZmlsZT0iZGF0YXNldHMvcGVha3NfY2hhcmFjdGVyaXN0aWNzLnJkcyIpIA0KDQoNCnJlcyA8LSBkZl9jYWxpYnJhdGVkICU+JQ0KICAgIGZpbHRlcihkYXRlPj1hcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJFN0YXJ0KSx0ej0iVVRDIiksIGRhdGU8PWFzLlBPU0lYY3QocGFzdGUoIjIwMTktMDktMDUiLCBwZWFrc1sxLF0kRW5kKSx0ej0iVVRDIikpICU+JQ0KICAgIGdyb3VwX2J5KHNlbnNvcikgJT4lDQogIGZpbHRlcihQTTI1Pj0xKSAlPiUNCiAgc3VtbWFyaXNlKG1heF92YWx1ZSA9IG1heChQTTI1X2NvcnIpKSAlPiUNCiAgICBtdXRhdGUoRXhwZXJpbWVudCA9IHBlYWtzWzEsXSRFeHBlcmltZW50LCBTb3VyY2UgPSBwZWFrc1sxLF0kU291cmNlLCBWYXJpYXRpb249IHBlYWtzWzEsXSRWYXJpYXRpb24sIE51bWJlciA9IHBlYWtzWzEsXSROdW1iZXIpDQpmb3Iocm93IGluIDI6bnJvdyhwZWFrcykpew0KICBkZl9jYWxpYnJhdGVkICU+JQ0KICAgIGZpbHRlcihkYXRlPj1hcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3Nbcm93LF0kU3RhcnQpLHR6PSJVVEMiKSwgZGF0ZTw9YXMuUE9TSVhjdChwYXN0ZSgiMjAxOS0wOS0wNSIsIHBlYWtzW3JvdyxdJEVuZCksdHo9IlVUQyIpKSAlPiUNCiAgICBncm91cF9ieShzZW5zb3IpICU+JQ0KICAgICAgZmlsdGVyKFBNMjU+PTEpICU+JQ0KICBzdW1tYXJpc2UobWF4X3ZhbHVlID0gbWF4KFBNMjVfY29ycikpICU+JQ0KICAgIG11dGF0ZShFeHBlcmltZW50ID0gcGVha3Nbcm93LF0kRXhwZXJpbWVudCwgU291cmNlID0gcGVha3Nbcm93LF0kU291cmNlLCBWYXJpYXRpb249IHBlYWtzW3JvdyxdJFZhcmlhdGlvbiwgTnVtYmVyID0gcGVha3Nbcm93LF0kTnVtYmVyKSAtPiB0bXANCiAgcmVzPC1yYmluZChyZXMsdG1wKQ0KICANCn0gDQpyZXMgJT4lDQogIGlubmVyX2pvaW4ocGVha3MsIGJ5PWMoIlNvdXJjZSIsICJFeHBlcmltZW50IiwgIlZhcmlhdGlvbiIsICJOdW1iZXIiKSkgLT5yZXNfYmluZA0KDQpyZXNfYmluZDwtcmVzX2JpbmQgJT4lDQogIG11dGF0ZShyYXRpbyA9IG1heF92YWx1ZS9Db25jZW50cmF0aW9uKQ0KDQoNCg0KcmVzX2JpbjwtY1NwbGl0KGluZHQ9cmVzX2JpbmQsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMykNCg0KDQpgYGANCg0KDQojIENvbXBhcmUgd2l0aCB1bmNhbGlicmF0ZWQgZGF0YQ0KDQpgYGB7cn0NCg0KcmVzIDwtIGRmICU+JQ0KICBmaWx0ZXIoZGF0ZSA+PSBhcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJFN0YXJ0KSwgdHogPSAiVVRDIiksIA0KICAgICAgICAgZGF0ZSA8PSBhcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJEVuZCksIHR6ID0gIlVUQyIpKSAlPiUNCiAgZ3JvdXBfYnkoc2Vuc29yKSAlPiUNCiAgZmlsdGVyKFBNMjUgPj0gMSkgJT4lDQogIHN1bW1hcmlzZShtYXhfdmFsdWUgPSBtYXgoUE0yNSkpICU+JQ0KICBtdXRhdGUoRXhwZXJpbWVudCA9IHBlYWtzWzEsIF0kRXhwZXJpbWVudCwgU291cmNlID0gcGVha3NbMSwgXSRTb3VyY2UsIFZhcmlhdGlvbiA9IHBlYWtzWzEsIF0kVmFyaWF0aW9uLCBOdW1iZXIgPSBwZWFrc1sxLCBdJE51bWJlcikNCmZvcihyb3cgaW4gMjpucm93KHBlYWtzKSl7DQogIGRmICU+JQ0KICAgIGZpbHRlcihkYXRlID49IGFzLlBPU0lYY3QocGFzdGUoIjIwMTktMDktMDUiLCBwZWFrc1tyb3csIF0kU3RhcnQpLCB0eiA9ICJVVEMiKSwgDQogICAgICAgICAgIGRhdGUgPD0gYXMuUE9TSVhjdChwYXN0ZSgiMjAxOS0wOS0wNSIsIHBlYWtzW3JvdywgXSRFbmQpLCB0eiA9ICJVVEMiKSkgJT4lDQogICAgZ3JvdXBfYnkoc2Vuc29yKSAlPiUNCiAgICBmaWx0ZXIoUE0yNSA+PSAxKSAlPiUNCiAgc3VtbWFyaXNlKG1heF92YWx1ZSA9IG1heChQTTI1KSkgJT4lDQogIG11dGF0ZShFeHBlcmltZW50ID0gcGVha3Nbcm93LCBdJEV4cGVyaW1lbnQsIFNvdXJjZSA9IHBlYWtzW3JvdywgXSRTb3VyY2UsIA0KICAgICAgICAgVmFyaWF0aW9uPSBwZWFrc1tyb3csIF0kVmFyaWF0aW9uLCBOdW1iZXIgPSBwZWFrc1tyb3csIF0kTnVtYmVyKSAtPiB0bXANCiAgcmVzPC1yYmluZChyZXMsdG1wKQ0KICANCn0gDQpyZXNfYmluZF91bmNhbGlicmF0ZWQ8LSByZXMgJT4lDQogIGlubmVyX2pvaW4ocGVha3MsIGJ5ID0gYygiU291cmNlIiwgIkV4cGVyaW1lbnQiLCAiVmFyaWF0aW9uIiwgIk51bWJlciIpKSAgJT4lDQogIG11dGF0ZShyYXRpbyA9IG1heF92YWx1ZS9Db25jZW50cmF0aW9uKQ0KDQoNCnJlc19iaW5kJHN0YXR1cyA8LSAiQ2FsaWJyYXRlZCINCnJlc19iaW5kX3VuY2FsaWJyYXRlZCRzdGF0dXMgPC0gIk5vdCBDYWxpYnJhdGVkIg0KcmVzX2NvbXBhcmlzb24gPC0gcmJpbmQocmVzX2JpbmQsIHJlc19iaW5kX3VuY2FsaWJyYXRlZCkNCg0KZGYyPC1jU3BsaXQoaW5kdD1yZXNfY29tcGFyaXNvbixzcGxpdENvbHMgPSBjKCJzZW5zb3IiKSxzZXA9Ii0iLGRpcmVjdGlvbj0id2lkZSIsZHJvcD1GQUxTRSkNCg0KZGYyICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMyktPnJlc19jb21wYXJpc29uDQoNCg0KDQpgYGANCg0KYGBge3J9DQoNCnJlc19jb21wYXJpc29uJHNlbnNvcl8xIDwtIGZhY3RvcihyZXNfY29tcGFyaXNvbiRzZW5zb3JfMSwgbGV2ZWxzID0gYygiUE1TNTAwMyIsICJTUFMwMzAiLCAiSFBNQTExNVMwIiwgIlNEUzAxOCIsICJPUENSMSIpKQ0KDQpyZXNfY29tcGFyaXNvbiRzdGF0dXMgPC0gZmFjdG9yKHJlc19jb21wYXJpc29uJHN0YXR1cywgbGV2ZWxzID0gYygiTm90IENhbGlicmF0ZWQiLCAiQ2FsaWJyYXRlZCIpKQ0KcmVzX2NvbXBhcmlzb24gJT4lDQogIGZpbHRlcihWYXJpYXRpb24gPT0gIlBlYWsiKSAlPiUNCiAgZ2dwbG90KGFlcyh4PUV4cGVyaW1lbnQseT1yYXRpbyxmaWxsPXN0YXR1cykpICsgDQogICBnZW9tX2JveHBsb3Qod2lkdGg9MC44LHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSxhbHBoYT0wLjgsbHdkPTAuMyxmYXR0ZW49MC44LG91dGxpZXIuc2l6ZSA9IDAuOCxvdXRsaWVyLmFscGhhPTAuNSxvdXRsaWVyLnN0cm9rZT0wKSsNCiAgZmFjZXRfd3JhcChzZW5zb3JfMX5Tb3VyY2UsbmNvbD00KSt5bGFiKCJSYXRpbyBzZW5zb3IvZHVzdHRyYWsiKSt0aGVtZV9idygpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eShjKC0xLDYpLCBuPTE0KSxzZWMuYXhpcyA9IGR1cF9heGlzKG5hbWU9TlVMTCkpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCxzaXplID0gNikpLT5wDQpwX2xlZ2VuZDwtZ2V0X2xlZ2VuZChwKQ0KDQpwbG90X2dyaWQocF9sZWdlbmQpDQoNCnA8LXArdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KcDwtcCsgIHRoZW1lKHBhbmVsLnNwYWNpbmcgPSB1bml0KDAsICJsaW5lcyIpKSArDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAibGluZXMiKSkNCnANCiNnZ3NhdmUocCxmaWxlbmFtZSA9ICJyYXRpb19jb21wYXJpc29uX3NwYWNpbmcuc3ZnIix1bml0ID0gIm1tIiwgd2lkdGg9IDE4MCwgaGVpZ2h0ID0gMjQwKQ0KIyhhc19nZ3Bsb3QocF9sZWdlbmQpLGZpbGVuYW1lID0gInJhdGlvX2NvbXBhcmlzb25fbGVnZW5kLnN2ZyIsdW5pdCA9ICJtbSIsIHdpZHRoPSAxODAsIGhlaWdodCA9IDI0MCkNCg0KDQpgYGANCg0KDQoNCg0K